package com.walle.ak47.commons.utils;

import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@SuppressWarnings({"unchecked","rawtypes"})
public class ChineseUtils
{
	public static class CharInfo
	{
		private char ch ;
		private String[] pinyins ;
		private String pinyinStr ;
		
		public CharInfo( char _ch , String[] _pinyins )
		{
			this.ch = _ch ;
			this.pinyins = _pinyins ;
			if( _pinyins != null && _pinyins.length > 0 )
			{
				pinyinStr = _pinyins[0] ;
				for( int i=1 ; i<_pinyins.length ; i++ )
					pinyinStr += "/" + _pinyins[i] ;
			}
		}

		public char getCh()
		{
			return ch;
		}

		public String[] getPinyins()
		{
			return pinyins;
		}
		
		public void setPinyin( String _pinyin )
		{
			if( pinyins != null && pinyins.length > 0 )
			{
				for( int i=0 ; i<pinyins.length ; i++ )
				{
					if( pinyins[i].equalsIgnoreCase( _pinyin ) )
						return ;
				}
			}
			
			if( pinyins == null || pinyins.length <= 0 )
			{
				pinyins = new String[1] ;
				pinyins[0] = _pinyin ;
				pinyinStr = _pinyin ;
			}
			else
			{
				String[] newArr = new String[ pinyins.length+1] ;
				System.arraycopy( pinyins , 0 , newArr , 0 , pinyins.length ) ;
				newArr[newArr.length-1] = _pinyin ;
				pinyinStr += "/" + _pinyin ;
				pinyins = newArr ;
			}
		}
		
		public String getPinyinStr()
		{
			return this.pinyinStr ;
		}
	}
	
	private static Logger LOG = LoggerFactory.getLogger( ChineseUtils.class ) ;
	private static ChineseUtils.CharInfo[] characters = new ChineseUtils.CharInfo[0xFFFF+1] ; 
	
	private static void init_gbk()
	{
		LOG.info( "Init pinyin table..." ) ;
		Enumeration<URL> enumer = ClassUtils.getResources( "pinyin_gbk.txt" ) ;
		if( enumer != null )
		{
			while( enumer.hasMoreElements() )
			{
				URL url = (URL)enumer.nextElement() ;
				
				try
				{
					String[] temps = StringUtils.split( FileUtils.getFileText( url , "GBK" ) , "\r\n" ) ;

					for( int i=0 ; i<temps.length ; i++ )
					{
						String temp = temps[i].trim() ;
						String[] substrs = StringUtils.split( temp , "," ) ;
						if( substrs.length < 2 || substrs[0].length() < 1 )
							continue ;
						char ch = substrs[0].charAt(0) ;
						String[] pinyins = new String[substrs.length-1] ;
						for( int j=1 ; j<substrs.length ; j++ )
							pinyins[j-1] = substrs[j] ;

						characters[ch] = new ChineseUtils.CharInfo( ch , pinyins );
					}
					
				}
				catch( Throwable err )
				{
					LOG.error( null , err ) ;
				}
			}
		}
		
		LOG.info( "Pinyin table has been loaded." ) ;
	}
	
	static
	{
		init_gbk() ;
	}
	
	public static void makePinyinTableFile( String _fileName )
	{
		StringBuffer buffer = new StringBuffer() ;
		for( int i=0 ; i<characters.length ; i++ )
		{
			CharInfo charInfo = characters[i] ;
			if( charInfo == null )
				continue ;
			String[] pinyins = charInfo.getPinyins() ;
			if( pinyins == null )
				continue ;
			if( pinyins.length > 1 )
				continue ;

			buffer.append( charInfo.getCh() ) ;
			for( int j=0 ; j<pinyins.length ; j++ )
				buffer.append( "," + pinyins[j] ) ;

			buffer.append( "\r\n" ) ;
		}
		
		FileUtils.writeFileText( _fileName , buffer.toString() , "GBK" ) ;
	}
	
	public static String[] getPinyins( char _ch )
	{
		if( _ch > 255 )
		{
			CharInfo charInfo = characters[_ch] ;
			if( charInfo == null )
				return null ;
			else
				return charInfo.getPinyins() ;
		}
		else
			return new String[]{ String.valueOf( _ch ) };
	}
	
	public static String getPinyin( char _ch )
	{
		if( _ch > 255 )
		{
			CharInfo charInfo = characters[_ch] ;
			if( charInfo == null )
				return null ;
			else
				return charInfo.getPinyinStr() ;
		}
		else
			return String.valueOf( _ch ) ;
	}
	
	public static String[] toMultiPinyinStr( String _src , String _split )
	{
		String[] pinyins = new String[2] ;
		
		if( _src == null || _src.length() <= 0 )
			return null ;
		if( _split == null )
			_split = "" ;
		
		boolean singleChar = false ;
		StringBuffer buffer = new StringBuffer() ;
		StringBuffer buffer2 = new StringBuffer() ;
		String[] prevPinyins = null ;
		
		try
		{
			char[] charBuffer = _src.toCharArray() ;
			int length = charBuffer.length ;
			for( int i=0 ; i<length ; i++ )
			{
				char ch = charBuffer[i] ;
				if( ch > 255 && ch < 0xFFFF )
				{
					if( singleChar )
					{
						buffer.append( _split ) ;
						singleChar = false ;
					}

					CharInfo charInfo = characters[ch] ;
					String[] chPinyins = null ;
					
					if( charInfo == null )
						buffer.append( ch ) ;
					else
					{
						chPinyins = charInfo.getPinyins() ;
						if( chPinyins == null || chPinyins.length <= 0 )
							buffer.append( ch + _split ) ;
						else
							buffer.append( chPinyins[0] + _split ) ;
					}
					
					if( chPinyins != null && chPinyins.length > 1 )
					{
						String[] nextPinyins = null ;
						if( i<length-1 )
						{
							char nextCh = charBuffer[i+1] ;
							if( nextCh > 255 && nextCh < 0xFFFF && characters[nextCh] != null )
								nextPinyins = characters[nextCh].getPinyins() ;
						}
						
						byte multiFlag = 0 ;
						
						if( prevPinyins != null && prevPinyins.length > 1 )
							multiFlag += 4 ;
						if( chPinyins != null && chPinyins.length > 1 )
							multiFlag += 2 ;
						if( nextPinyins != null && nextPinyins.length > 1 )
							multiFlag += 1 ;
						
						switch( multiFlag )
						{
						case 0x2:
							for( int temploop2=1 ; temploop2<chPinyins.length ; temploop2++ )
							{
								if( prevPinyins != null )
									buffer2.append( prevPinyins[0] + " " ) ;
								buffer2.append( chPinyins[temploop2] ) ;
								if( nextPinyins != null )
									buffer2.append( " " + nextPinyins[0] + "," ) ;
								else
									buffer2.append( ',' ) ;
							}
							break ;
						case 0x3:
							for( int temploop2=0 ; temploop2<chPinyins.length ; temploop2++ )
							{
								for( int temploop3=0 ; temploop3<nextPinyins.length ; temploop3++ )
								{
									if( temploop2 == 0 && temploop3 == 0 )
										continue ;
									if( prevPinyins != null )
										buffer2.append( prevPinyins[0] + " " ) ;
									buffer2.append( chPinyins[temploop2] + " " ) ;
									buffer2.append( nextPinyins[temploop3] + ',' ) ;
								}
							}
							break ;
						case 0x6:
							if( nextPinyins != null )
							{
								for( int temploop1=0 ; temploop1<prevPinyins.length ; temploop1++ )
								{
									for( int temploop2=0 ; temploop2<chPinyins.length ; temploop2++ )
									{
										if( temploop1==0 && temploop2==0 )
											continue ;
										buffer2.append( prevPinyins[temploop1] + " " ) ;
										buffer2.append( chPinyins[temploop2] ) ;
										if( nextPinyins != null )
											buffer2.append( " " + nextPinyins[0] + "," ) ;
										else
											buffer2.append( ',' ) ;
									}
								}
							}
							break ;
						case 0x7:
							for( int temploop1=0 ; temploop1<prevPinyins.length ; temploop1++ )
							{
								for( int temploop2=0 ; temploop2<chPinyins.length ; temploop2++ )
								{
									for( int temploop3=0 ; temploop3<nextPinyins.length ; temploop3++ )
									{
										if( temploop1==0 && temploop2==0 && temploop3==0)
											continue ;
										buffer2.append( prevPinyins[temploop1] + " " ) ;
										buffer2.append( chPinyins[temploop2] + " " ) ;
										buffer2.append( nextPinyins[temploop3] + "," ) ;
									}
								}
							}
							break ;
						}
					}
					
					prevPinyins = chPinyins ;
				}
				else
				{
					buffer.append( ch ) ;
					singleChar = true ;
					prevPinyins = null ;
				}
			}
		}
		catch( Exception err )
		{
			throw new RuntimeException( err ) ;
		}
		
		pinyins[0] = buffer.toString() ;
		pinyins[1] = buffer2.toString() ;
		
		return pinyins ;
	}
	
	public static String toPinyinStr( String _src , String _split )
	{
		if( _src == null || _src.length() <= 0 )
			return "" ;
		if( _split == null )
			_split = "" ;
		
		boolean singleChar = false ;
		StringBuffer buffer = new StringBuffer() ;
		try
		{
			char[] charBuffer = _src.toCharArray() ;
			int length = charBuffer.length ;
			for( int i=0 ; i<length ; i++ )
			{
				char ch = charBuffer[i] ;
				if( ch > 255 && ch < 0xFFFF )
				{
					if( singleChar )
					{
						buffer.append( _split ) ;
						singleChar = false ;
					}

					CharInfo charInfo = characters[ch] ;

					if( charInfo == null )
						buffer.append( ch ) ;
					else
					{
						String[] charPinyins = charInfo.getPinyins() ;
						
						if( charPinyins == null || charPinyins.length <= 0 )
							buffer.append( ch + _split ) ;
						else
							buffer.append( charPinyins[0] + _split ) ;
					}
				}
				else
				{
					buffer.append( ch ) ;
					singleChar = true ;
				}
			}
		}
		catch( Exception err )
		{
			throw new RuntimeException( err ) ;
		}
		
		return buffer.toString() ;
	}

	public static String summarize( String _src )
	{
		StringBuffer buffer = new StringBuffer() ;
		if( _src == null || _src.length() <= 0 )
			return "" ;
		
		int charCount = 0 ;
		int dbcsCount = 0 ;
		int[] pinyinCounts = new int[20] ;
		Map mapMultiPinyin = new HashMap() ;
		
		for( int i=0 ; i<pinyinCounts.length ; i++ )
			pinyinCounts[i] = 0 ;
		
		try
		{
			char[] charBuffer = _src.toCharArray() ;
			int length = charBuffer.length ;
			charCount = length ;
			
			for( int i=0 ; i<length ; i++ )
			{
				char ch = charBuffer[i] ;
				if( ch > 255 && ch < 0xFFFF )
				{
					dbcsCount++ ;
					
					CharInfo charInfo = characters[ch] ;
					
					if( charInfo != null )
					{
						String[] charPinyins = charInfo.getPinyins() ;
						if( charPinyins != null && charPinyins.length > 0 )
						{
							pinyinCounts[charPinyins.length-1] ++ ;
							if( charPinyins.length > 1 )
							{
								if( mapMultiPinyin.containsKey( "" + ch ) )
								{
									Integer counter = (Integer)mapMultiPinyin.get( "" + ch ) ;
									counter = new Integer( counter.intValue()+1 ) ;
									mapMultiPinyin.put( "" + ch , counter ) ;
								}
								else
									mapMultiPinyin.put( "" + ch , new Integer(1) ) ;
							}
						}
					}
				}
			}
		}
		catch( Exception err )
		{
			throw new RuntimeException( err ) ;
		}
		
		buffer.append( "TotalChars=" + charCount + "\n" ) ;
		buffer.append( "DBCharCount=" + dbcsCount + "\n" ) ;
		buffer.append( "DBCharRate=" + 0.1*dbcsCount/charCount*10 + "\n" ) ;
		int totalMultiPinyin = 0 ; 
		int totalPinyin = 0 ;
		for( int i=0 ; i<pinyinCounts.length ; i++ )
		{
			totalPinyin += pinyinCounts[i] ;
			if( i>0 )
				totalMultiPinyin += pinyinCounts[i] ;
			if( pinyinCounts[i] > 0 )
				buffer.append( String.valueOf(i+1) + " PinyinCount=" + pinyinCounts[i] + "\n" ) ;
		}
		
		buffer.append( "MultiPinyinRate=" + 0.1*totalMultiPinyin/totalPinyin*10 + "\n" ) ;
		buffer.append( "MultiPinyinDetails=\n" ) ;
		Iterator iter = mapMultiPinyin.entrySet().iterator() ;
		while( iter.hasNext() )
		{
			Map.Entry entry = (Map.Entry)iter.next() ;
			buffer.append( "\t" + (String)entry.getKey() + "\t" + (Integer)entry.getValue() + "\n" ) ;
		}
		
		return buffer.toString() ;
	}
}
